﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Atendex.Dominio;
using Atendex.Containex;
using Atendex.Infraestrutura.Repositórios;
using System.Reflection;
using Atendex.Dominio.Interfaces;

namespace Atendex.Testes
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class TesteRepositórios
    {
        RepositorioAtendente repAtendente = new RepositorioAtendente();
        RepositorioAtendimento repAtendimento = new RepositorioAtendimento();
        RepositorioOrdemServico repSolicitacao = new RepositorioOrdemServico();
        RepositorioSolicitante repRequerente = new RepositorioSolicitante();
        RepositorioUnidadeNegocio repAreaNegocio = new RepositorioUnidadeNegocio();
        RepositorioCurso repCurso = new RepositorioCurso();
        RepositorioTipoSolicitacaoAN repTipoSolicitacao = new RepositorioTipoSolicitacaoAN();
        RepositorioMeio repMeio = new RepositorioMeio();

        public TesteRepositórios()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {
            InicializadorRepositorio.InicializaRepositorio();
        }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion
        Random rnd = new Random();

        #region Métodos auxiliares
        /// <summary>
        /// Cadastra uma área de negócio e retorna um identificador único
        /// </summary>
        /// <returns></returns>
        protected string CadastraAreaNegocio()
        {
            string payload = rnd.Next().ToString();
            IUnidadeNegocio area = Container.CriarUnidadeNegocio(payload,
                Container.CriarResponsavel("teste", "teste@teste"));
            repAreaNegocio.Salvar(area);
            // verifica
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Área de Negócio não foi cadastrada.");
            return payload;
        }
        protected IUnidadeNegocio ObtemAreaNegocio(string payload)
        {
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Área de Negócio não existe.");
            return repAreaNegocio.ObtemPorPropriedade("Nome", payload)[0];
        }

        protected void ExcluirAreaNegocio(string payload)
        {
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Área de Negócio não pode ser excluída porque não existe.");
            repAreaNegocio.Excluir(repAreaNegocio.ObtemPorPropriedade("Nome", payload)[0]);
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 0,
                "Área de Negócio não foi excluída.");
        }

        protected string CadastraTipoSolicitacao(IUnidadeNegocio area)
        {
            string payload = rnd.Next().ToString();
            ITipoSolicitacaoAN tipo = Container.CriarTipoSolicitacaoAN(payload, "qwe123", area);
            repTipoSolicitacao.Salvar(tipo);
            // verifica
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Tipo de Solicitação não foi cadastrada.");
            return payload;
        }
        protected ITipoSolicitacao ObtemTipoSolicitacao(string payload)
        {
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Tipo de Solicitação não existe.");
            return repTipoSolicitacao.ObtemPorPropriedade("Nome", payload)[0];
        }
        protected void ExcluirTipoSolicitacao(string payload)
        {
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Tipo de Solicitação não pode ser excluída porque não existe.");
            repTipoSolicitacao.Excluir(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload)[0]);
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 0,
                "Tipo de Solicitação não foi excluída.");
        }

        protected string CadastraAtendente()
        {
            string payload = rnd.Next().ToString();
            IAtendente tipo = Container.CriarAtendente("Josimara", "email", payload, "senha");
            repAtendente.Salvar(tipo);
            // verifica
            IAtendente[] atendente = repAtendente.ObtemPorPropriedade("Login", payload);
            Assert.IsTrue(atendente.Length >= 1,
                "Atendente não foi cadastrada.");
            return payload;
        }
        protected IAtendente ObtemAtendente(string payload)
        {
            IAtendente[] atendentes =  repAtendente.ObtemPorPropriedade("Login", payload);
            Assert.IsTrue(atendentes.Length == 1,
                "Atendente não existe.");
            return atendentes[0];
        }

        protected void ExcluirAtendente(string payload)
        {
            Assert.IsTrue(repAtendente.ObtemPorPropriedade("Login", payload).Length == 1,
                "Atendente não pode ser excluída porque não existe.");
            repAtendente.Excluir(repAtendente.ObtemPorPropriedade("Login", payload)[0]);
            Assert.IsTrue(repAtendente.ObtemPorPropriedade("Login", payload).Length == 0,
                "Atendente não foi excluída.");
        }

        protected string CadastraMeioContato()
        {
            string payload = rnd.Next().ToString();
            IMeio meio = Container.CriarMeioContato(payload);
            repMeio.Salvar(meio);
            // verifica
            Assert.IsTrue(repMeio.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Meio de contato não foi cadastrado");
            return payload;
        }

        protected IMeio ObtemMeioContato(string nome)
        {
            IMeio[] meios = repMeio.ObtemPorPropriedade("Nome", nome);
            Assert.IsTrue(meios.Length == 1,
                "Meio de contato não existe.");
            return meios[0];
        }

        protected void ExcluiMeioContato(string nome)
        {
            repMeio.Excluir(ObtemMeioContato(nome));
            Assert.IsTrue(repMeio.ObtemPorPropriedade("Nome", nome).Length == 0,
                "Meio de contato não foi excluído.");
        }

        protected string CadastraAtendimento(IAtendente atendente, ITipoSolicitacao tiposolicitacao, IMeio meioContato)
        {
            // esse método é diferente dos acima, pois ele constrói um atendimento, requerimento
            // e uma solicitação, mas só salva o atendimento
            string payloadReq = rnd.Next().ToString();
            string payloadMsg = rnd.Next().ToString();
            ISolicitante req = Container.CriarSolicitante(payloadReq, meioContato);
            IAtendimento atendimento = Container.CriarAtendimento(atendente);
            IOrdemServico solicitacao = Container.CriarOrdemServico(atendimento, tiposolicitacao,
                Container.CriarMensagem(payloadMsg));

            atendimento.Solicitante = req;
            atendimento.OrdensServico.Add(solicitacao);

            repAtendimento.Salvar(atendimento);
            // verifica
            Assert.IsTrue(repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).Count() == 1,
                "Atendimento não foi cadastrada.");
            Assert.IsTrue(repRequerente.ObtemPorPropriedade("Nome", payloadReq).Length == 1,
                "Requerente não foi cadastrado.");
            Assert.IsTrue(repSolicitacao.ObtemTodosNaoAtivos().Where(s => s.Mensagens[0].Texto == payloadMsg).Count() == 1,
                "Solicitação não foi cadastrada.");
            return payloadMsg;
        }
        protected IAtendimento ObtemAtendimento(string payloadMsg)
        {
            Assert.IsTrue(repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).Count() == 1,
                "Atendimento não existe.");
            return repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).First();
        }
        protected void ExcluirAtendimento(string payloadMsg)
        {
            Assert.IsTrue(repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).Count() == 1,
                "Atendimento não existe.");
            string payloadReq = repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).First().Solicitante.Nome;
            
            repAtendimento.Excluir(repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).First());
            Assert.IsTrue(repAtendimento.ObtemTodosNaoAtivos().Where(s => s.OrdensServico[0].Mensagens[0].Texto == payloadMsg).Count() == 0,
                "Atendimento não foi excluída.");
            Assert.IsTrue(repRequerente.ObtemPorPropriedade("Nome", payloadReq).Length == 0,
                "Requerente não foi excluído.");
            Assert.IsTrue(repSolicitacao.ObtemTodosNaoAtivos().Where(s => s.Mensagens[0].Texto == payloadMsg).Count() == 0,
                "Solicitação não foi excluída.");
        }
        #endregion

        [TestMethod]
        public void TesteRepositórioActiveRecord()
        {
            /* Sequencia: cadastra um atendente / verifica
              *            cadastra uma área de negócios / verifica
              *            cadastra um tipo de solicitação / verifica
              *            cadastra um atendimento com solicitação, requerente e mensagem
              *            verifica que cadastrou
              *            exclui tudo
              *            verifica que excluiu */
            string idAtendente = CadastraAtendente();
            string idAN = CadastraAreaNegocio();
            string idTipo = CadastraTipoSolicitacao(ObtemAreaNegocio(idAN));
            string idMeio = CadastraMeioContato();
            string idAtend = CadastraAtendimento(ObtemAtendente(idAtendente), ObtemTipoSolicitacao(idTipo),
                ObtemMeioContato(idMeio));
            ExcluirAtendente(idAtendente);
            // o antendimento deve ter sido excluido junto
            Assert.IsTrue(repAtendimento.ObtemTodosNaoAtivos().Where(at => at.Atendente.Login == idAtend).Count() == 0,
                "Atendimento não foi excluído junto com atendente.");
            ExcluirAreaNegocio(idAN);
            // o tipo da solicitação deve ter sido excluído junto
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", idTipo).Length == 0,
                "Tipo de solicitação não foi excluído junto com área de negócio");
            ExcluiMeioContato(idMeio);
        }

        [TestMethod]
        public void TesteAreaNegocio()
        {
            string payload = rnd.Next().ToString();
            IUnidadeNegocio area = Container.CriarUnidadeNegocio(payload, Container.CriarResponsavel(payload, payload));
            repAreaNegocio.Salvar(area);
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Área de negócios não foi cadastrada");
            IUnidadeNegocio areadb = repAreaNegocio.ObtemPorPropriedade("Nome", payload)[0];
            Assert.IsTrue(areadb.Nome == payload);
            repAreaNegocio.Excluir(areadb);
            Assert.IsTrue(repAreaNegocio.ObtemPorPropriedade("Nome", payload).Length == 0,
                "Área de negócios não foi excluída");
        }

        [TestMethod]
        public void TesteAreaNegocioCurso()
        {
            string payload = rnd.Next().ToString();
            ICurso area = Container.CriarCurso(payload, new List<IResponsavel>{
                Container.CriarResponsavel("paul", payload),
            Container.CriarResponsavel("george", payload),
            Container.CriarResponsavel("john", payload),
            Container.CriarResponsavel("ringo", payload)});

            repCurso.Salvar(area);
            Assert.IsTrue(repCurso.ObtemPorPropriedade("Nome", payload).Length == 1,
                "Curso não foi cadastrado");
            ICurso areadb = repCurso.ObtemPorPropriedade("Nome", payload)[0];
            Assert.IsTrue(areadb.Responsaveis.Count == 4);
            Assert.IsTrue(areadb.Nome == payload);
            repCurso.Excluir(areadb);
            Assert.IsTrue(repCurso.ObtemPorPropriedade("Nome", payload).Length == 0,
                "Curso não foi excluído");
        }

        [TestMethod]
        public void TesteTipoSolicitacao()
        {
            string payload = rnd.Next().ToString();
            IUnidadeNegocio area = Container.CriarUnidadeNegocio(payload, Container.CriarResponsavel(payload,payload));
            repAreaNegocio.Salvar(area);

            ITipoSolicitacaoAN tipo = Container.CriarTipoSolicitacaoAN(payload, "1234", area);
            repTipoSolicitacao.Salvar(tipo);

            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 1, "Tipo de solicitação de AN não foi cadastrada");

            ITipoSolicitacaoAN tipodb = repTipoSolicitacao.ObtemPorPropriedade("Nome", payload)[0];

            Assert.IsTrue(tipodb.AreaNegocio.Nome == payload, "Area de negocio não foi cadastrada");

            repTipoSolicitacao.Excluir(tipodb);
            Assert.IsTrue(repTipoSolicitacao.ObtemPorPropriedade("Nome", payload).Length == 0, "Tipo de solicitação de AN não foi excluído");
            repAreaNegocio.Excluir(repAreaNegocio.ObtemPorPropriedade("Nome", payload)[0]);
        }
        
        

        [TestMethod]
        public void TesteTipoSolicitacaoAreaErrada()
        {
            string payload = rnd.Next().ToString();
            ICurso curso = Container.CriarCurso(payload, new List<IResponsavel>{
                Container.CriarResponsavel(payload, payload) });
            IUnidadeNegocio un = Container.CriarUnidadeNegocio(payload, Container.CriarResponsavel(payload, payload));
            try
            {
                ITipoSolicitacaoAN tipo = Container.CriarTipoSolicitacaoAN(payload, "", un);
                tipo.AreaNegocio = curso;
                Assert.Fail("Um tipo de solicitação de AN pôde ser criado para um curso");
            }
            catch (DominioException)
            {
            }
            try
            {
                ITipoSolicitacao tipo = Container.CriarTipoSolicitacaoCurso(payload, "");
                tipo.AreaNegocio = un;
                Assert.Fail("Um tipo de solicitação de Curso pôde ser criado para uma AN");
            }
            catch (DominioException)
            {
            }
        }

        [TestMethod]
        public void TesteOrdemServico()
        {
            string payload = rnd.Next().ToString();
            IAtendente atendente = Container.CriarAtendente(payload, "email",  "123", "123");

            IAtendimento atendimento = Container.CriarAtendimento(atendente);

            ICurso meuCurso1 = Container.CriarCurso("qwe123", new List<IResponsavel>{
                Container.CriarResponsavel(payload, payload) });
            ICurso meuCurso2 = Container.CriarCurso("qwe123", new List<IResponsavel>{
                Container.CriarResponsavel(payload, payload) });

            ITipoSolicitacao tipoCurso = Container.CriarTipoSolicitacaoCurso(payload, "");

            try
            {
                IOrdemServico ordem = Container.CriarOrdemServico(atendimento, tipoCurso,
                    Container.CriarMensagem(payload));
                Assert.Fail("Foi possível adicionar um tipo de solicitação sem área de negócios à ordem de serviço.");
            }
            catch (DominioException)
            { }
            tipoCurso.AreaNegocio = meuCurso1;
            IOrdemServico ordems = Container.CriarOrdemServico(atendimento,tipoCurso,
                Container.CriarMensagem(payload));
            try
            {
                ordems.Tipos.Add(tipoCurso);
                Assert.Fail("Foi possível inserir um tipo de solicitação utilizando o IList<ITipoSolicitacao>");
            }
            catch (NotSupportedException)
            { }

            try
            {
                tipoCurso.AreaNegocio = meuCurso2;
                ordems.AcrescentarTipo(tipoCurso);
                Assert.Fail("Foi possível acrescentar um tipo de solicitação de uma área de negócios diferente.");
            }
            catch (DominioException) { }
        }

        [TestMethod]
        public void TesteAtendenteRepetido()
        {
            IAtendente atendente = Container.CriarAtendente("Paul McCartney", "email",  "qwe123456", "qwe123456");
            repAtendente.Salvar(atendente);
            try
            {
                IAtendente atendente2 = Container.CriarAtendente("Paul McCartney", "email",  "qwe123456", "qwe123456");
                repAtendente.Salvar(atendente2);
                Assert.Fail("Um atendente com login repetido pôde ser cadastrado.");
            }
            catch (RepositorioException)
            {
                Assert.IsTrue(true);
            }
            finally
            {
                IAtendente atendentetemp = repAtendente.ObtemPorPropriedade("Nome", "Paul McCartney")[0];
                repAtendente.Excluir(atendentetemp);
            }
        }
    }
}
